﻿

namespace Tentacle.Scheduler
{
    using System;
    using System.Linq;
    using System.Collections.Generic;
    using System.Text;
    using System.Net;
    using System.Threading.Tasks;

    //
    using Tentacle.Core;
    using Tentacle.Downloader;
    using Tentacle.Pipeline;
    using Tentacle.Processor;

    //
    using static Tentacle.Utils.Hepler;

    //
    using SchedulerTask = System.Threading.Tasks.Task;
    using SchedulerTaskState = System.Boolean;
    using WebRequest = Tentacle.Core.WebRequest;


    /// <summary>
    /// 内存调度器
    /// </summary>
    public sealed class MemoryScheduler : IScheduler
    {

#if DEBUG
        int traceCount = 0;
        int wait = 0;
#endif


        #region Fields

        RemoteNodeManager   _connectionPool;
        Queue<WebSite>      _taskQueue;
        Queue<WebSite>      _recentQueue;
        HashSet<WebSite>    _namedTable;
        HashSet<WebSite>    _uniqueQueue;

        IPageProcessor[]    _processors;
        IWebPipeline        _pipeline;
        bool                _setProcessorPendding;
        SchedulerTask       _task;
        SchedulerTaskState  _state;
        SchedulerTaskState  _pause;
        #endregion

        public MemoryScheduler()
        {
            _connectionPool = new RemoteNodeManager();
            _taskQueue = new Queue<WebSite>();
            _recentQueue = new Queue<WebSite>();
            _namedTable = new HashSet<WebSite>();
            _uniqueQueue = new HashSet<WebSite>();

            _connectionPool.DownloadCompleted += OnDownloadCompleted;
            _connectionPool.DownloadFailed += OnDownloadFailed;
        }

        #region Property

        /// <summary>
        /// 
        /// </summary>
        public int PeroidTime { get; set; }

        /// <summary>
        /// 
        /// </summary>
        public IEnumerable<IDownloader> Downloader {
            get {
                return _connectionPool;
            }
            set {

            }
        }

        /// <summary>
        /// 
        /// </summary>
        public Queue<WebSite> TaskQueue {
            get {
                return _taskQueue;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public Queue<WebSite> RecentTask {
            get {
                return _recentQueue;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public IEnumerable<WebSite> FinishedPool {
            get {
                return _namedTable;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public IEnumerable<IPageProcessor> Processors {
            get {
                return _processors;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public WebToken Token { get; set; }

        /// <summary>
        /// 
        /// </summary>
        public WebProxy Proxy { get; set; }

        public int FinishedCount {
            get {
                return _namedTable.Count;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public DownloadFailedHandler DownloadFailed { get; set; }

        /// <summary>
        /// 
        /// </summary>
        public DownloadCompletedHandler DownloadCompleted { get; set; }
        #endregion

        #region Public Method

        public void SetPageProcessor(IPageProcessor processor)
        {
            if (!_setProcessorPendding) {
                _setProcessorPendding = true;
                if (processor == null) {
                    throw new NullReferenceException();
                }
                _processors = new IPageProcessor[] { processor };
            }
        }

        public void SetPageProcessor(IPageProcessor[] processors)
        {
            if (!_setProcessorPendding) {
                _setProcessorPendding = true;
                _processors = processors ?? throw new NullReferenceException();
            }
        }

        public void SetPipeline(IWebPipeline pipeline)
        {
            _pipeline = pipeline ?? throw new NullReferenceException();
        }

        public void Start(WebSite startPage)
        {
            if (!_state) {
                _state = true;
                _taskQueue.Enqueue(startPage);
                _task = SchedulerTask.Factory.StartNew(SchedulerMainLoop);
            }
        }

        #endregion

        #region Downloader Callback

        void OnDownloadCompleted(WebPage page)
        {
            if (_processors == null || _processors.Length == 0) {
                throw new InvalidOperationException("未指定PageProcessor");
            }
            if (_processors.Length == 1) {
                var global = _processors.First();
                var result =  global.Process(page);

                // sniff
                foreach (var task in result) {
                    lock (_taskQueue) {
                        if (_uniqueQueue.Add(task))
                            _taskQueue.Enqueue(task);
                    }
                }

                // process
                if (_pipeline != null) {
                    _pipeline.Store(global.Resources);
                }
            }
            else {
                IPageProcessor pageProcessor = null;

                // find processor
                foreach (var processor in _processors) {
                    if (processor.IsPageMatch(page)) {
                        pageProcessor = processor;
                        break;
                    }
                }

                if (pageProcessor == null) {
                    return;
                }

                pageProcessor.Process(page);
            }

            DownloadCompleted?.Invoke(page);
        }

        void OnDownloadFailed(string errorMessage)
        {
            DownloadFailed?.Invoke(errorMessage);
        }

        #endregion

        #region Scheduler 

        async void SchedulerMainLoop()
        {
            while (_state) {
                if (wait == 30) {
                    wait = 0;
                    Trace($"TaskQueue Length:{_taskQueue.Count}\nFinish Queue Length:{_namedTable.Count}");
                }
                WebSite site = null;
                while (_taskQueue.Count > 0) {
                    lock (_taskQueue)
                        site = _taskQueue.Dequeue();
                    if (site == null) {
                        Trace($"TaskQueue Length:{_taskQueue.Count}");
                    }
                    if (site != null && _namedTable.Add(site)) {
                        break;
                    }
                }

                while (true) {
                    await SchedulerTask.Delay(1000);
                    wait++;
                    IDownloader downloader = Schedule();
                    if (downloader != null) {
                        downloader.WaitTime = PeroidTime;
                        if(!downloader.SetTask(new WebRequest(site, Token))) {
                            Rollback(site);
                            _connectionPool.Remove((RemoteNode)downloader);
                        }
                        break;
                    }
                }

                //                if (_taskQueue.Count > 0 && !_pause) {
                //                    WebSite site = null;
                //                    var downloader = Schedule();

                //                    if(downloader != null) {
                //                        lock (_taskQueue) {
                //                             site = _taskQueue.Dequeue();
                //                        }
                //                        if (_namedTable.Add(site)) {

                //                            downloader.WaitTime = PeroidTime;
                //                            downloader.SetTask(new WebRequest(site, Token));
                //                        }
                //                    }
                //                }
            }
        }

        IDownloader Schedule()
        {
            List<IDownloader> list = new List<IDownloader>();

            foreach (var downloader in _connectionPool) {
                if (downloader.CanDownload) {
                    if (downloader.WaitTime == 0) {
                        list.Add(downloader);
                    }
                    else {
                        downloader.WaitTime -= (downloader.WaitTime >= 1000) ? 1000 : downloader.WaitTime;
                    }
                }
            }

            return list.Count == 0 ? null : (from e in list orderby e.ElapseTime select e).First();
        }

        public void Rollback(WebSite site)
        {
            _pause = true;
            lock (_taskQueue) {
                _taskQueue.Enqueue(site);
            }
            _pause = false;
        }

        public void RebuildProgress(string[] sites, string[] nametable)
        {
            _pause = true;
            lock (_taskQueue) {
                foreach (var site in sites) {
                    _taskQueue.Enqueue(new WebSite(site));
                }
            }

            lock (_namedTable) {
                foreach (var name in nametable) {
                    _namedTable.Add(new WebSite(name));
                }
            }
            _pause = false;
        }

        #endregion

        #region Pipeline Callback

        #endregion
    }
}
